package com.lzh.excel.imports.listener;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhehen.lu
 * @date 2025/8/3 14:31
 */
public class ErrorWriteExcelImportListener<T> extends BaseAbstractImportListener<T> {
    private static final Logger log = LoggerFactory.getLogger(ErrorWriteExcelImportListener.class);
    protected int batchWriteCount = 2000;
    protected List<List<String>> errorHeadList = new ArrayList(16);
    protected List<List<Object>> errorDataList;
    private ExcelWriter excelWriter;
    private WriteSheet writeSheet;
    private boolean alreadyCreateExcel;
    private String fileName;
    private ByteArrayOutputStream errorFileSteam;
    private int errorDataLine;

    public ErrorWriteExcelImportListener() {
        this.errorDataList = new ArrayList(this.batchWriteCount);
        this.alreadyCreateExcel = false;
        this.errorDataLine = 0;
        this.setCheckParamsType(CheckParamsType.ERROR_RECORD);
    }

    public boolean isHaveError() {
        return this.alreadyCreateExcel;
    }

    public int getSuccessDataLine() {
        int successDataLine = this.totalDataLineNum - this.errorDataLine;
        return successDataLine > 0 ? successDataLine : 0;
    }

    public InputStream getErrorExcelStream() throws IOException {
        if (this.isHaveError()) {
            ByteArrayInputStream var1;
            try {
                var1 = new ByteArrayInputStream(this.errorFileSteam.toByteArray());
            } finally {
                this.errorFileSteam.close();
            }

            return var1;
        } else {
            return null;
        }
    }

    @Override
    protected void invokeErrorData(T importBo, AnalysisContext context, List<String> errorMsgList) {
        this.addErrorData(importBo, errorMsgList);
        this.writeBatchData(context);
    }

    @Override
    protected void invokeException(Exception exception, AnalysisContext context, String errorMsg) {
        this.addErrorData(context.readRowHolder().getCellMap(), errorMsg);
        this.writeBatchData(context);
    }

    @Override
    protected void doFinishAllAnalysedAfter(AnalysisContext context) {
        if (CollectionUtils.isNotEmpty(this.errorDataList)) {
            this.doWriteData(context);
        }

        Optional.ofNullable(this.excelWriter).ifPresent((writer) -> {
            writer.finish();
        });
    }

    @Override
    protected void doSaveData(AnalysisContext context, List<T> data) {
    }

    @Override
    protected boolean checkParamsAndSetError(T importBO, AnalysisContext analysisContext, List<String> errorMagList) {
        return false;
    }

    @Override
    protected void invokeRealHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        List<List<String>> list = (List)headMap.values().stream().map((v) -> {
            List<String> item = new ArrayList();
            item.add(v.getStringValue() == null ? "" : v.getStringValue());
            return item;
        }).collect(Collectors.toList());
        List<String> item = new ArrayList();
        item.add("失败原因");
        this.errorHeadList.addAll(list);
        this.errorHeadList.add(item);
    }

    protected void addErrorData(T importBo, List<String> errorMsgList) {
        List<Object> list = new ArrayList();
        if (importBo instanceof Map) {
            Map<Object, Object> map = (Map)importBo;
            list = (List)map.values().stream().collect(Collectors.toList());
        } else if (importBo.getClass() != null) {
            list = (List) Arrays.stream(importBo.getClass().getDeclaredFields()).map((field) -> {
                field.setAccessible(true);

                try {
                    return field.get(importBo);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    return null;
                }
            }).collect(Collectors.toList());
        }

        this.addErrorDataProcessing((List)list, (String)errorMsgList.stream().collect(Collectors.joining(",")));
    }

    protected void addErrorData(Map<Integer, Cell> cellMap, String errorMsg) {
        new ArrayList();
        List<Object> list = (List)cellMap.values().stream().map((v) -> {
            return ((ReadCellData)v).getStringValue();
        }).collect(Collectors.toList());
        this.addErrorDataProcessing(list, errorMsg);
    }

    protected void addErrorDataProcessing(List<Object> list, String errorMsg) {
        if (list.size() >= this.errorHeadList.size()) {
            list = list.subList(0, this.errorHeadList.size() - 1);
        } else if (list.size() < this.errorHeadList.size() - 1) {
            while(list.size() < this.errorHeadList.size() - 1) {
                list.add("");
            }
        }

        list.add(errorMsg);
        this.errorDataList.add(list);
        ++this.errorDataLine;
    }

    private void writeBatchData(AnalysisContext context) {
        if (this.errorDataList.size() >= this.batchWriteCount) {
            this.doWriteData(context);
        }

    }

    private void doWriteData(AnalysisContext context) {
        this.createErrorExcel(context);
        this.excelWriter.write(this.errorDataList, this.writeSheet);
        this.errorDataList.clear();
    }

    private void createErrorExcel(AnalysisContext context) {
        if (!this.alreadyCreateExcel) {
            if (StringUtils.isEmpty(this.fileName)) {
                String rootPath = "";

                try {
                    URL url = ResourceUtils.getURL("classpath:");
                    rootPath = url.getPath();
                } catch (FileNotFoundException e) {
                    log.error("获取写入文件路径失败:{}", JSON.toJSONString(e.getStackTrace()));
                }

                StringBuilder nameStringBuilder = new StringBuilder();
                nameStringBuilder.append(rootPath).append("错误【").append(System.currentTimeMillis()).append("】");
                if (context.readWorkbookHolder().getFile() != null) {
                    nameStringBuilder.append(context.readWorkbookHolder().getFile().getName());
                } else {
                    nameStringBuilder.append(".xlsx");
                }

                this.fileName = nameStringBuilder.toString();
            }

            this.errorFileSteam = new ByteArrayOutputStream();
            this.excelWriter = ((ExcelWriterBuilder) EasyExcel.write(this.errorFileSteam).head(this.errorHeadList)).build();
            this.writeSheet = EasyExcel.writerSheet().build();
            this.alreadyCreateExcel = true;
        }
    }

    public String getFileName() {
        return this.fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public int getErrorDataLine() {
        return this.errorDataLine;
    }
}
